package com.yixing.tech.wms.service.impl;

import com.yixing.tech.common.base.BusinessException;
import com.yixing.tech.wms.entity.InStockWork;
import com.yixing.tech.wms.param.ContainerBindMaterialParam;
import com.yixing.tech.wms.param.ToApplyParam;
import com.yixing.tech.wms.service.SalesReturnStockService;
import com.yixing.tech.wms.utils.DocumentUtils;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.yixing.tech.common.CommonConstant.*;
import static com.yixing.tech.common.CommonConstant.InStatus.*;

/**
 * 原材料入库服务实现
 *
 * @author yixing tech
 * @since 1.0.0
 */
@Slf4j
@Service
public class SalesReturnStockServiceImpl extends AbstractInStockService implements SalesReturnStockService {

    /**
     * 根据容器编码申请库位，申请当前空余的空位
     * 优先级排序：1. 无库存组。 有库存组：深度越大越优先
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Document applyLocationByContainerCode(String containerCode) throws BusinessException {
        return doApplyLocationByContainerCode(containerCode);
    }

    /**
     * 根据多个原材料入库单申请容器
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<InStockWork> applyContainerByInOrders(List<String> inOrders, String abc) throws BusinessException {
        return doApplyContainerByInOrders(inOrders, SALES_RETURN_STOCK, SALES_RETURN_STOCK_DTL, abc);
    }


    @Override
    public List<Document> listToApplyParam(ToApplyParam param) throws BusinessException {
        return doListToApplyParam(param, SALES_RETURN_STOCK, SALES_RETURN_STOCK_DTL);
    }

    /**
     * 查看申请成功的入库单明细
     */
    @Override
    public List<Document> listInStoking(ToApplyParam param) {
        return doListInStoking(param, Arrays.asList(InStockType.salesReturnStock.getCode()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean containerBindMaterialByBindCode(String bindCode, int realNum, String partitionSide) throws BusinessException {
        if (realNum <= 0) {
            return false;
        }

        Document bind = simpleFindOne("entity_" + BIND_CONTAINER, "code", bindCode);
        if (bind == null) {
            throw new BusinessException("容器绑定数据不存在");
        }
        String optType = DocumentUtils.unwrapDictItem(bind.get("optType", Document.class));
        if (!OptType.toBind.getCode().equals(optType)) {
            throw new BusinessException("必须是【未绑定】状态下才能进行绑定");
        }

        List<Document> inOrders = bind.getList("inOrders", Document.class);
        List<String> taskCodes = inOrders.stream().map(x -> x.getString("taskCode")).collect(Collectors.toList());
        List<InStockWork> inStockTasks = mongoTemplate.find(Query.query(
                        newCriteria().and("code").in(taskCodes).and("inStatus").is(newDocument("id", new ObjectId(applyOK.getCode())))),
                InStockWork.class, "entity_" + IN_STOCK_TASK);
        if (CollectionUtils.isEmpty(inStockTasks)) {
            throw new BusinessException("数据非法，上架作业不存在");
        }

        // 根据入库单进行分组，匹配的数量之和就是最大数量
        double maxNum = 0D;
        for (InStockWork inStockTask : inStockTasks) {
            maxNum += inStockTask.getMatchNum();
        }
        if (realNum > maxNum) {
            throw new BusinessException("实际数量不能大于推荐数量");
        }

        Update update = new Update();
        update.set("realNum", realNum);
        update.set("isBind", true);
        update.set("optType", newDocument("id", new ObjectId(OptType.bind.getCode())));
        if (!updateById(bind, update, "entity_" + BIND_CONTAINER)) {
            return false;
        }

        // 依次更新实际数量
        double realNumTemp = realNum;
        for (InStockWork inStockTask : inStockTasks) {
            Update update2 = new Update();
            update2.set("inStatus", newDocument("id", new ObjectId(InStatus.containerBind.getCode())));
            double match = inStockTask.getMatchNum();
            if (realNumTemp >= match) {
                update2.set("realNum", match);
                inStockTask.setRealNum(match);
                realNumTemp -= match;
            } else {
                update2.set("realNum", realNumTemp);
                inStockTask.setRealNum(realNumTemp);
                realNumTemp = 0D;
            }
            updateById(inStockTask.get_id().toString(), update2, "entity_" + IN_STOCK_TASK);
        }

        return true;
    }

    /**
     * 绑定物料到容器，申请库位，启动入库
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean startStock(ContainerBindMaterialParam param) throws BusinessException {
        return doStartStock(param);
    }

    /**
     * 查询容器带绑定的信息
     */
    @Override
    public Object findContainerToBind(String containerCode) throws BusinessException {
        return doFindContainerToBind(containerCode);
    }

    /**
     * 查询容器带绑定的信息，通过库位
     */
    @Override
    public Object findContainerToBindByLocation(String locationCode) throws BusinessException {
        return doFindContainerToBindByLocation(locationCode);
    }

    /**
     * 查询容器带绑定的信息
     */
    @Override
    public Object findContainerToBindById(String bindId) throws BusinessException {
        return doFindContainerToBindById(bindId);
    }

    @Override
    public boolean reportErp(String dtlId) throws BusinessException {
        return doReportErp(dtlId, SALES_RETURN_STOCK, SALES_RETURN_STOCK_DTL);
    }

}
